package com.socket.server.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.socket.core.constant.ChatConstants;
import com.socket.core.constant.ChatProperties;
import com.socket.core.model.command.enmus.Group;
import com.socket.secure.util.Assert;
import com.socket.server.custom.exception.BizException;
import com.socket.server.custom.publisher.CommandPublisher;
import com.socket.server.mapper.SysGroupMapper;
import com.socket.server.model.base.BasePo;
import com.socket.server.model.enmus.ProessState;
import com.socket.server.model.po.SysGroup;
import com.socket.server.model.po.SysProess;
import com.socket.server.service.SysGroupService;
import com.socket.server.service.SysProessService;
import com.socket.server.util.Bcrypt;
import com.socket.server.util.ShiroUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class SysGroupServiceImpl extends ServiceImpl<SysGroupMapper, SysGroup> implements SysGroupService {
    private final SysProessService sysProessService;
    private final CommandPublisher publisher;
    private final ChatProperties properties;
    private final ChatConstants constants;

    public String createGroup(String groupName, String password) {
        String userId = ShiroUser.getUserId();
        // 创建检查
        boolean expression = StrUtil.isEmpty(password) || password.length() <= properties.getMaxGroupPassword();
        Assert.isTrue(expression, "密码长度不合法", BizException::new);
        LambdaQueryWrapper<SysGroup> check = Wrappers.lambdaQuery();
        check.eq(SysGroup::getOwner, userId);
        long count = count(check);
        Assert.isTrue(count <= properties.getMaxCreateGroupNum(), "群组创建已达上限", BizException::new);
        // 必要的组名检查
        Assert.isFalse(StrUtil.isEmpty(groupName), "空的群组名称", BizException::new);
        Assert.isTrue(StrUtil.length(groupName) <= 8, "无效的群组名称", BizException::new);
        LambdaQueryWrapper<SysGroup> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(SysGroup::getName, groupName);
        Assert.isNull(getFirst(wrapper), "群组名称已存在", BizException::new);
        // 写入数据库
        SysGroup group = new SysGroup();
        String gid = constants.getGroupPrefix() + RandomUtil.randomNumbers(properties.getGenerateGuidLength());
        group.setGuid(gid);
        group.setName(groupName);
        group.setOwner(userId);
        // 散列密码
        if (StrUtil.isNotEmpty(password)) {
            group.setPassword(Bcrypt.digest(password));
        }
        if (super.save(group)) {
            // 推送事件
            publisher.pushGroupEvent(group, Group.CREATE);
            // 加入新建的群组里
            joinGroup(gid, userId, true);
            return gid;
        }
        return null;
    }

    public boolean removeUser(String gid, String uid) {
        String stater = ShiroUser.getUserId();
        LambdaQueryWrapper<SysGroup> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(SysGroup::getGuid, gid);
        SysGroup group = getFirst(wrapper);
        // 权限检查
        Assert.isTrue(group.getOwner().equals(stater), "你不是此群的创建者", BizException::new);
        boolean delete = sysProessService.delete(uid, gid);
        // 推送事件
        if (delete) {
            SysProess user = new SysProess(gid, uid);
            publisher.pushGroupEvent(user, Group.REMOVE);
            return true;
        }
        return false;
    }

    @Override
    public void joinGroup(String gid, String uid, String password) {
        LambdaQueryWrapper<SysGroup> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(SysGroup::getGuid, gid);
        SysGroup group = getFirst(wrapper);
        Assert.notNull(group, "找不到群组", BizException::new);
        String hash = group.getPassword();
        if (StrUtil.isNotEmpty(hash)) {
            Assert.isTrue(Bcrypt.verify(password, hash), "入群密码不正确", BizException::new);
        }
        joinGroup(gid, uid, group.isRelease());
    }

    @Override
    public void joinGroup(String gid, String uid, boolean release) {
        boolean apply = sysProessService.apply(uid, gid, null);
        // 在这里直接通过申请
        if (release && apply) {
            apply = sysProessService.state(gid, uid, ProessState.RESOLVE);
        }
        // 推送事件
        if (apply) {
            SysProess user = new SysProess(gid, uid);
            publisher.pushGroupEvent(user, Group.JOIN);
        }
    }

    public boolean dissolveGroup(String gid) {
        String userId = ShiroUser.getUserId();
        LambdaUpdateWrapper<SysGroup> wrapper = Wrappers.lambdaUpdate();
        wrapper.eq(SysGroup::getGuid, gid);
        wrapper.eq(SysGroup::getOwner, userId);
        wrapper.set(BasePo::isDeleted, 1);
        if (super.update(wrapper)) {
            // 推送事件
            SysGroup group = new SysGroup();
            group.setGuid(gid);
            group.setOwner(userId);
            publisher.pushGroupEvent(group, Group.DISSOLVE);
            return true;
        }
        return false;
    }

    @Override
    public boolean exitGroup(String gid) {
        String userId = ShiroUser.getUserId();
        boolean delete = sysProessService.delete(userId, gid);
        if (delete) {
            // 推送事件
            SysProess user = new SysProess(gid, userId);
            publisher.pushGroupEvent(user, Group.EXIT);
            return true;
        }
        return false;
    }

    @Override
    public boolean updatePassword(String gid, String password) {
        String userId = ShiroUser.getUserId();
        LambdaUpdateWrapper<SysGroup> wrapper = Wrappers.lambdaUpdate();
        wrapper.eq(SysGroup::getGuid, gid);
        SysGroup group = getFirst(wrapper);
        Assert.notNull(group, "找不到群组ID: " + gid, BizException::new);
        boolean equals = group.getOwner().equals(userId);
        Assert.isTrue(equals, "权限不足", BizException::new);
        String digestPass = StrUtil.isEmpty(password) ? null : Bcrypt.digest(password);
        wrapper.set(SysGroup::getPassword, digestPass);
        return update(wrapper);
    }
}
